﻿using LightCAD.MathLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace LightCAD.MathLib
{
    public class JSON
    {
        //public static JObject parse(string jsonString)
        //{
        //    return JObject.Parse(jsonString);
        //}
        //public static string stringify(JObject jobj, bool indented = false)
        //{
        //    if (indented)
        //        return jobj.ToString(Newtonsoft.Json.Formatting.Indented);
        //    else
        //        return jobj.ToString();
        //}
    }
    public class JsObjTest
    {
        public static int counter;
    }
    public class JsObj<T, K> : Dictionary<T, K>
    {
        public K get(T key)
        {
            K t = default(K);
            this.TryGetValue(key, out t);
            return t;
        }
        public new K this[T t]
        {
            get { return get(t); }
            set { this.set(t, value); }
        }
        public JsObj<T, K> set(T key, K value)
        {
            if (this.ContainsKey(key))
                base[key] = value;
            else
                this.Add(key, value);
            return this;
        }
        public JsObj<T, K> remove(T key)
        {
            if (this.ContainsKey(key))
            {
                base.Remove(key);
            }
            return this;
        }
        public bool has(T key)
        {
            return this.ContainsKey(key);
        }

        public void forEach(Action<T, object> action)
        {
            foreach (var kvp in this)
            {
                action(kvp.Key, kvp.Value);
            }
        }
        public void setKeyValues(params object[] keyValues)
        {
            for (var i = 0; i < keyValues.Length; i += 2)
            {
                this.Add((T)keyValues[i], (K)keyValues[i + 1]);
            }
        }
        public void setObject(object obj)
        {
            if (obj == null) return;
            var fields = obj.GetFields();
            for (var i = 0; i < fields.Length; i += 1)
            {
                this.Add((T)(object)fields[i], (K)obj.GetField(fields[i]));
            }
        }
        public void delete(T key)
        {
            this.remove(key);
        }
        public JsObj<T, TRtn> toJsObj<TRtn>(Func<KeyValuePair<T, K>, T> keyGetter, Func<KeyValuePair<T, K>, TRtn> valGetter)
        {
            var result = new JsObj<T, TRtn>();
            foreach (var item in this)
            {
                var key = keyGetter(item);
                var val = valGetter(item);
                result[key] = val;
            }
            return result;
               
        }
        public JsObj<T, K> clone()
        {
            var nobj = new JsObj<T, K>();
            foreach (var kvp in this)
            {
                nobj.Add(kvp.Key, kvp.Value);
            }
            return nobj;
        }

    }
    public class JsObj<T> :IDictionary<string,T>
    {
        private Dictionary<string, T> _data = new Dictionary<string, T>();

        public ICollection<string> Keys => _data.Keys;

        public ICollection<T> Values => _data.Values;

        public int Count => _data.Count;

        public bool IsReadOnly => false;

        public virtual T get(string name)
        {
            //JsObjTest.counter++;
            T t = default(T);
            _data.TryGetValue(name, out t);
            return t;
        }
        public virtual bool tryGet(string name,out T t)
        {
            //JsObjTest.counter++;
            t = default(T);
            return _data.TryGetValue(name, out t);
        }
        public virtual JsObj<T> set(string name, T value)
        {
            //JsObjTest.counter++;
            if (this.ContainsKey(name))
                _data[name] = value;
            else
                _data.Add(name, value);
            return this;
        }
        public virtual JsObj<T> remove(string name)
        {
            //JsObjTest.counter++;
            if (this.ContainsKey(name))
            {
                _data.Remove(name);
            }
            return this;
        }
        public  bool has(string name)
        {
            //JsObjTest.counter++;
            return this.ContainsKey(name);
        }
        public T this[string key]
        {
            get { return this.get(key); }
            set { this.set(key, value); }
        }
        public void forEach(Action<object, T> action)
        {
            foreach (var kvp in this)
            {
                action(kvp.Key, kvp.Value);
            }
        }
        public void setNameValue(params object[] nameValues)
        {
            for (var i = 0; i < nameValues.Length; i += 2)
            {
                this.Add((string)nameValues[i], (T)nameValues[i + 1]);
            }
        }
        public void setObject(object obj)
        {
            if (obj == null) return;
            var fields = obj.GetFields();
            for (var i = 0; i < fields.Length; i += 1)
            {
                _data.Add(fields[i], (T)obj.GetField(fields[i]));
            }
        }
        public void delete(string key)
        {
            this.remove(key);
        }
        public JsObj<T> clone()
        {
            var nobj = new JsObj<T>();
            foreach (var kvp in this)
            {
                nobj.Add(kvp.Key, kvp.Value);
            }
            return nobj;
        }

        public bool ContainsKey(string key)
        {
            return _data.ContainsKey(key);
        }

        public void Add(string key, T value)
        {
            this.set(key, value);
        }

        [Obsolete("不得调用", true)]
        public bool Remove(string key)
        {
            this.remove(key);
            return true;
        }

        public bool TryGetValue(string key, out T value)
        {
            value = this.get(key);
            return value!=null;
        }

        [Obsolete("不得调用",true)]
        public void Add(KeyValuePair<string, T> item)
        {
            _data.Add(item.Key,item.Value);
        }

        public virtual void Clear()
        {
            _data.Clear();
        }

        public bool Contains(KeyValuePair<string, T> item)
        {
            return _data.Contains(item);
        }

        public void CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
        {
            throw new Exception("Copy");
        }

        public bool Remove(KeyValuePair<string, T> item)
        {
            return _data.Remove(item.Key);
        }

        public IEnumerator<KeyValuePair<string, T>> GetEnumerator()
        {
            return _data.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _data.GetEnumerator();
        }
        public JsObj<TRtn> toJsObj<TRtn>(Func<KeyValuePair<string,T>, string> keyGetter, Func<KeyValuePair<string,T>, TRtn> valGetter)
        {
            var result = new JsObj<TRtn>();
            foreach (var item in this)
            {
                var key = keyGetter(item);
                var val = valGetter(item);
                result[key] = val;
            }
            return result;

        }
    }
   

    public class JsObj : JsObj<object,object>
    {
        public T _<T>(string name)
        {
            return (T)this.get(name);
        }
        public JsObj _<T>(string name, T value)
        {
            this.set(name, value);
            return this;
        }
        public JsObj _o(string name)
        {
            return (JsObj)this.get(name);
        }
        public JsObj _s(string name, JsObj value)
        {
            this.set(name, value);
            return this;
        }
        public JsArr _a(string name)
        {
            return (JsArr)this.get(name);
        }
        public JsObj _a(string name, JsArr value)
        {
            this.set(name, value);
            return this;
        }
        public string _s(string name)
        {
            return (string)this.get(name);
        }
        public JsObj _s(string name, string value)
        {
            this.set(name, value);
            return this;
        }
        public int _i(string name)
        {
            return (int)this.get(name);
        }
        public JsObj _i(string name, int value)
        {
            this.set(name, value);
            return this;
        }
        public double _f(string name)
        {
            return (double)this.get(name);
        }
        public JsObj _f(string name, double value)
        {
            this.set(name, value);
            return this;
        }
        public bool _b(string name)
        {
            return (bool)this.get(name);
        }
        public JsObj _b(string name, bool value)
        {
            this.set(name, value);
            return this;
        }

        internal static JsObj from(object parameters)
        {
            var obj = new JsObj();
            var props = parameters.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo prop in props)
            {
                obj.Add(prop.Name, prop.GetValue(parameters));
            }
            return obj;
        }
    }

    public class JsArr : ListEx<object>
    {

        public T _<T>(int index)
        {
            return (T)this.Get(index);
        }
        public JsArr _<T>(int index, T value)
        {
            this.Set(index, value);
            return this;
        }
        public JsObj _o(int index)
        {
            return (JsObj)this.Get(index);
        }
        public JsArr _s(int index, JsObj value)
        {
            this.Set(index, value);
            return this;
        }
        public JsArr _a(int index)
        {
            return (JsArr)this.Get(index);
        }
        public JsArr _a(int index, JsArr value)
        {
            this.Set(index, value);
            return this;
        }
        public string _s(int index)
        {
            return (string)this.Get(index);
        }
        public JsArr _s(int index, string value)
        {
            this.Set(index, value);
            return this;
        }
        public int _i(int index)
        {
            return (int)this.Get(index);
        }
        public JsArr _i(int index, int value)
        {
            this.Set(index, value);
            return this;
        }
        public double _f(int index)
        {
            return (double)this.Get(index);
        }
        public JsArr _f(int index, double value)
        {
            this.Set(index, value);
            return this;
        }
        public bool _b(int index)
        {
            return (bool)this.Get(index);
        }
        public JsArr _b(int index, bool value)
        {
            this.Set(index, value);
            return this;
        }

    }


    public static class ListExExt
    {
        public static ListEx<T> ToListEx<T>(this IEnumerable<T> source)
        {
            ListEx<T> jsArr = new ListEx<T>();
            foreach (var item in source)
            {
                jsArr.Push(item);
            }
            return jsArr;
        }
        public static bool Remove<TKey, TVal>(this Dictionary<TKey, TVal> dic, TKey key)
        {
            return dic.Remove(key);
        }
    }

    public static class JsDate
    {
        public static DateTime beginDate = new DateTime(1970, 1, 1);
        public static readonly DateTime timeOrign = DateTime.Now;
        public static long now() => (long)(DateTime.Now - beginDate).TotalMilliseconds;
        public static long performanceNow() => (long)((DateTime.Now - timeOrign).TotalMilliseconds);
    }

    public static class JsString
    {
        public static Match match(this string str, string pattern)
        {
            var m = Regex.Match(str, pattern);
            if (m.Success)
                return m;
            else
                return null;
        }
        public static string replace(this string str, string olvVal, string replace)
        {
            return str.Replace(olvVal, replace);
        }
        /// <summary>
        /// 带g正则修饰符的 js.string.replace()，会替换所有的匹配项
        /// </summary>
        /// <param name="str"></param>
        /// <param name="regex"></param>
        /// <param name="replace"></param>
        /// <returns></returns>
        public static string replace_g(this string str, Regex regex, string replace ) 
        {
            return regex.Replace(str, replace);
        }
        /// <summary>
        /// jsString.replace 默认只替换第一个匹配项如需替换所有实例，使用replaceg。
        /// </summary>
        /// <param name="str"></param>
        /// <param name="regex"></param>
        /// <param name="replace"></param>
        /// <returns></returns>
        public static string replace(this string str, Regex regex, string replace)
        {
            int index = -1;
            return regex.Replace(str, (m)=> 
            {
                index++;
                if (index == 0)
                    return replace;
                else
                    return m.Value;
            });
        }
        public static string replace(this string str, string pattern, MatchEvaluator match, RegexOptions regexOptions = RegexOptions.None)
        {
            return Regex.Replace(str, pattern, match, regexOptions);
        }
        public static ListEx<string> split(this string str, string splitStr)
        {
            var arr = new ListEx<string>();
            var items = str.Split(new string[] { splitStr }, StringSplitOptions.RemoveEmptyEntries);
            arr.AddRange(items);
            return arr;
        }
        public static string slice(this string str, int start, int end = int.MaxValue)
        {
            if (start < 0)
            {
                start = str.Length + start;
            }
            if (end < 0)
            {
                end = str.Length + end;
            }
            if (end - start <= 0) return "";
            return str.Substring(start, end - start);
        }
        public static int search(this string str, string pattern)
        {
            var m = Regex.Match(str, pattern);
            if (m.Success)
                return m.Index;
            else
                return -1;
        }
        public static int indexOf(this string str, string searchString, int position = 0)
        {
            return str.IndexOf(searchString, position);
        }
    }

    public class JsMap : Dictionary<object, object>
    {
        public bool has(object key)
        {
            return this.ContainsKey(key);
        }
    }

    //internal static class JsonExt 
    //{
    //    internal static JObject GetObject(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;

    //        return (JObject)prop.Value;
    //    }
    //    internal static JArray GetArray(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;
    //        return (JArray)prop.Value;
    //    }
    //    internal static double[] GetDoubleArray(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;
    //        var arr = (JArray)prop.Value;
    //        var result = new double[arr.Count];
    //        for (int i = 0; i < arr.Count; i++)
    //        {
    //            result[i] = Convert.ToDouble(arr[i]);
    //        }
    //        return result;
    //    }
    //    internal static int[] GetIntArray(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;
    //        var arr = (JArray)prop.Value;
    //        var result = new int[arr.Count];
    //        for (int i = 0; i < arr.Count; i++)
    //        {
    //            result[i] = Convert.ToInt32(arr[i]);
    //        }
    //        return result;
    //    }
    //    internal static String GetString(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;
    //        return (String)prop.Value;
    //    }
    //    internal static JsObj<object> GetJsObj(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null) return null;
    //        var jsObj = new JsObj<object>();
    //        var propVal = prop.Value as JObject;
    //        foreach (var item in propVal)
    //        {
    //            jsObj.Add(item.Key, item.Value);
    //        }
    //        return jsObj;
    //    }
    //    internal static bool GetBool(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        bool result = false;
    //        if (prop == null || prop.Value.Type == JTokenType.Null)
    //            return false;
    //        else
    //            return (bool)prop.Value;

    //    }
    //    internal static int GetInt(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null || prop.Value.Type == JTokenType.Null)
    //            return 0;
    //        else
    //            return (int)prop.Value;
    //    }
    //    internal static long GetLong(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null || prop.Value.Type == JTokenType.Null)
    //            return 0;
    //        else
    //            return (long)prop.Value;
    //    }
    //    internal static double GetDouble(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null || prop.Value.Type == JTokenType.Null)
    //            return 0;
    //        else
    //            return (double)prop.Value;
    //    }
    //    internal static DateTime GetDateTime(this JObject jo, string propName)
    //    {
    //        var prop = jo.Property(propName);
    //        if (prop == null || prop.Value.Type == JTokenType.Null)
    //            return DateTime.MinValue;
    //        else
    //        {
    //            var dt = (DateTime)prop.Value;
    //            return dt.ToLocalTime();
    //        }
    //    }
    //}

}